#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/printk.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

static int major;
static dev_t devnum;

static int read_flag = 0;

struct cdev hello_dev =
{
    .owner = THIS_MODULE,
};

static int hello_open(struct inode* i_node, struct file* f)
{
    printk(KERN_ALERT "hello open\r\n");
    read_flag = 0;
    return 0;
}

static int hello_close(struct inode* i_node, struct file* f)
{
    printk(KERN_ALERT "hello close\r\n");
    return 0;
}

static ssize_t hello_read(struct file* f, char __user* data, size_t size, loff_t* loff)
{
    printk(KERN_ALERT "hello read\r\n");
    if (0 == read_flag)
    {
        copy_to_user(data, "hello text\r\n", 13);
        return 13;
    }
    return 0;
}

static ssize_t hello_write(struct file* f, const char __user* data, size_t size, loff_t* loff)
{
    char* data_ker = kmalloc(size, GFP_KERNEL);

    if (data_ker)
    {
        copy_from_user(data_ker, data, size);
        printk(KERN_ALERT "hello write:%s\r\n", data_ker);
        kfree(data_ker);
        return size;
    }

    return -1;
}


struct file_operations hello_ops = {
    .open = hello_open,
    .release = hello_close,
    .read = hello_read,
    .write = hello_write,
};


static int __init hello_init(void)
{
    int ret = 0;
    printk(KERN_ALERT "hello module init\r\n");
    ret = alloc_chrdev_region(&devnum, 1, 1, "test driver");
    if (ret < 0)
    {
        printk(KERN_INFO "cdev_add fail: %d\r\n", ret);
        return ret;
    }

    cdev_init(&hello_dev, &hello_ops);

    ret = cdev_add(&hello_dev, devnum, 1);

    if (ret < 0)
    {
        printk(KERN_INFO "cdev_add fail: %d\r\n", ret);
        return ret;
    }

    printk(KERN_INFO "get major:%d\r\n", MAJOR(devnum));
    printk(KERN_INFO "get minor:%d\r\n", MINOR(devnum));

    return 0;
}

static void __exit hello_exit(void)
{
    printk(KERN_ALERT "hello module exit\r\n");
    cdev_del(&hello_dev);
    unregister_chrdev_region(&hello_dev, 1);
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_DESCRIPTION("test module");
MODULE_AUTHOR("zzh");
MODULE_LICENSE("GPL");
